﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using XingLucifer.Devices.Protocols;
using XingLucifer.IBase;
using XingLucifer.IBase.Enums;

namespace XingLucifer.Devices
{
    public class UDP_Fins : IDevices
    {
        private CancellationTokenSource _socketToken;
        private Socket _socket;
        private IBase.IProtocol _protocol;
        private byte _suffixIP;
        private string _ip;
        private int _port;
        public UDP_Fins(byte suffixIP, string ip, int port, CancellationTokenSource socketToken)
        {
            _suffixIP = suffixIP;
            _ip = ip;
            _port = port;
            _socketToken = socketToken;
        }
        public string Close()
        {
            string msg = $"{_ip}注销Socket";
            if (_socket != null)
            {
                try
                {
                    _socket.Close();
                    _socket.Dispose();
                    msg += "成功";
                }
                catch (Exception ex)
                {
                    msg += $"异常：{ex}";
                }
                _socket = null;
            }

            return msg ;
        }

        public bool Open()
        {
            try
            {

                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                _socket.ReceiveTimeout = 1000;
                _socket.SendTimeout = 1000;
                string[] ip = _ip.Split('.');
                _protocol = new UDP_Fins_Protocol(_suffixIP, Convert.ToByte(ip[3]));
                _socket.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Parse($"{ip[0]}.{ip[1]}.{ip[2]}.{_suffixIP}"), 0));
                _socket.Connect(new IPEndPoint(IPAddress.Parse(_ip), _port));
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public Task Read()
        {
            throw new NotImplementedException();
        }

        public TClass ReadClass<TClass>(int address, TClass obj = null, int count = 3) where TClass : class
        {
            if (obj == null) { obj = Activator.CreateInstance<TClass>(); }
            int classSize = (int)StructToBytes.GetClassSize(obj);
            if (classSize > 1998)
            {
                throw new Exception("超过Fins最大字节数");
            }
            byte[] bytes = new byte[7];
            bytes[0] = 0x01;
            bytes[1] = 0x82;
            bytes[2] = (byte)(address >> 8);
            bytes[3] = (byte)address;
            bytes[4] = 0;
            bytes[5] = (byte)(classSize >> 8);
            bytes[6] = (byte)classSize;
            bytes = SendToRead(bytes, count);
            if (bytes == null) 
            { 
                return default;
            }
            int boolSize = 0;
            StructToBytes.FromBytes(obj, bytes, ref boolSize, 0, EndianTypes.Fins);
            return obj;
        }

        public TValue ReadSingle<TValue>(int address, int length, int count = 3)
        {
            byte[] list = new byte[7];
            list[0] = 0x01;
            list[1] = 0x82;
            list[2] = (byte)(address >> 8);
            list[3] = (byte)address;
            list[4] = 0x00;
            Type type = typeof(TValue);
            if (type.IsArray)
            {
                throw new Exception("请使用Read<TValue>读取数组数据");
            }
            int count_length = 0;
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    count_length = 2;
                    break;
                case "Int64":
                case "UInt64":
                case "Double":
                    count_length = 4;
                    break;
                case "Int16":
                case "UInt16":
                    count_length = 1;
                    break;
                case "Byte":
                case "String":
                    count_length = length / 2;
                    break;
                case "Boolean":
                    throw new Exception($"没有实现 {type.Name}");
            }
            list[5] = (byte)(count_length >> 8);
            list[6] = (byte)count_length;
            byte[] bytes = SendToRead(list, 3);
            if (type.Name == "String")
            {
                for (int i = 3; i < bytes[0]; i = i + 2)
                {
                    byte t = bytes[i - 1];
                    bytes[i - 1] = bytes[i];
                    bytes[i] = t;
                }
                return (TValue)(object)Encoding.ASCII.GetString(bytes.Skip(2).Take(bytes[1] - 48).ToArray());
            }
            return (TValue)StructToBytes.GetValue(type, bytes, 0, EndianTypes.Fins);
        }

        public bool WriteClass<TClass>(TClass value, int address, int count = 3) where TClass : class
        {
            if (value == null) { throw new Exception("不要传递空对象"); }
            int classSize = (int)StructToBytes.GetClassSize(value);
            if (classSize > 1998)
            {
                throw new Exception("超过Fins最大字节数");
            }
            byte[] array = new byte[classSize];
            List<byte> list = new List<byte>
            {
                0x02,
                0x82,
                (byte)(address >> 8),
                (byte)address,
                0
            };
            int boolSize = 0;
            StructToBytes.ToBytes(value, array, ref boolSize, 0, EndianTypes.Fins);
            int length = classSize / 2;
            list.Add((byte)(length >> 8));
            list.Add((byte)length);
            list.AddRange(array);
            var bytes = SendToRead(list.ToArray(), 3);
            return bytes != null;
        }

        public bool WriteSingle(object value, int address, int offset, int length, int count = 3)
        {
            Type type = value.GetType();
            if (type.IsArray)
            {
                throw new Exception("请使用Write方法写入数组数据");
            }
            List<byte> list = new List<byte>
            {
                0x02,
                0x82,
                (byte)(address >> 8),
                (byte)address,
                (byte)offset
            };
            byte[] bytes = null;
            switch (type.Name)
            {
                case "Int32":
                case "UInt32":
                case "Single":
                    bytes = new byte[4];
                    break;
                case "Int64":
                case "UInt64":
                case "Double":
                    bytes = new byte[8];
                    break;
                case "Int16":
                case "UInt16":
                    bytes = new byte[2];
                    break;
                case "Byte":
                case "Boolean":
                    throw new Exception($"没有实现 {type.Name}");
                case "String":
                    byte[] code = Encoding.ASCII.GetBytes((string)value);
                    List<byte> list2 = new List<byte>
                    {
                        49,
                        (byte)(code.Length + 48)
                    };
                    list2.AddRange(code);
                    if (list2.Count % 2 != 0)
                    {
                        list2.Add(0);
                    }
                    for (int i = 3; i < list2.Count; i = i + 2)
                    {
                        byte t = list2[i - 1];
                        list2[i - 1] = list2[i];
                        list2[i] = t;
                    }
                    bytes = list2.ToArray();
                    break;
            }
            int length1 = (bytes.Length / 2);
            list.Add((byte)(length1 >> 8));
            list.Add((byte)length1);
            if (type.Name != "String")
            {
                StructToBytes.GetBytes(value, bytes, 0, EndianTypes.Fins);
            }
            list.AddRange(bytes);
            bytes = SendToRead(list.ToArray(), 3);
            return bytes != null;
        }

        #region Synchronize
        private volatile int _lock = 0;
        protected void Locked()
        {
            int lockNumber = 0;
            while (Interlocked.Exchange(ref _lock, 1) != 0)
            {
                _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0; _ = 0;
                if (++lockNumber > 50)
                {
                    Thread.Sleep(1);
                    lockNumber = 0;
                }
            }
        }

        protected void UnLock() => Interlocked.Exchange(ref _lock, 0);
        #endregion
        private byte[] SendToRead(byte[] bytes, int count)
        {
            for (int i = 0; i < count; i++)
            {
                try
                {
                    Locked();
                    _socket.Send(_protocol.Serialize(bytes));
                    List<byte> list = new List<byte>();
                    byte[] buffer = new byte[1024];
                    int length = _socket.Receive(buffer);
                    list.AddRange(buffer.Take(length));
                    while (!_protocol.Verify(list))
                    {
                        length = _socket.Receive(buffer);
                        list.AddRange(buffer.Take(length));
                    }
                    UnLock();
                    return _protocol.Deserialize(list);
                }
                catch (Exception ex)
                {
                    UnLock();
                    if (count == 1
                        || (_socketToken != null && _socketToken.Token.IsCancellationRequested)
                        || ex.ToString().Contains("远程主机关闭"))
                    {
                        throw ex;
                    }
                }
            }
            return default;
        }
    }
}
